/**
 * This includes all the functions you can use to communicate with our messages API
 *
 * @module Messages Methods
 */
import {
  CreateMessageReqPayload,
  EditMessageReqPayload,
  GetAllTopicMessagesData,
  GetMessageByIdData,
  GetToolFunctionParamsReqPayload,
  RegenerateMessageReqPayload,
} from "../../fetch-client";
import { TrieveSDK } from "../../sdk";

/**
 * Create a message. Messages are attached to topics in order to coordinate memory of gen-AI chat sessions.Auth’ed user or api key must have an admin or owner role for the specified dataset’s organization.
 * 
 * Example:
 * ```js
 *const data = await trieve.createMessage({
  topic_id: "3c90c3cc-1d76-27198-8888-8dd25736052a",
  new_message_content: "a new message"
});
 * ```
 */
export async function createMessage(
  /** @hidden */
  this: TrieveSDK,
  data: CreateMessageReqPayload,
  signal?: AbortSignal,
  parseHeaders?: (headers: Record<string, string>) => void
) {
  if (!this.datasetId) {
    throw new Error("datasetId is required");
  }

  return await this.trieve.fetch(
    "/api/message",
    "post",
    {
      data,
      datasetId: this.datasetId,
    },
    signal,
    parseHeaders
  );
}

export const getCleanFetch = () => {
  const iframe = document.createElement("iframe");
  iframe.style.display = "none";
  document.body.appendChild(iframe);
  const cleanFetch = iframe.contentWindow?.fetch.bind(window);
  document.body.removeChild(iframe);
  return cleanFetch;
};

/**
 * Create a message as a stream and returns a reader. Messages are attached to topics in order to coordinate memory of gen-AI chat sessions.Auth’ed user or api key must have an admin or owner role for the specified dataset’s organization.
 * 
 * Example:
 * ```js
 *const reader = await trieve.createMessageReader({
  topic_id: "3c90c3cc-1d76-27198-8888-8dd25736052a",
  new_message_content: "a new message"
});
 * ```
 */
export async function createMessageReader(
  /** @hidden */
  this: TrieveSDK,
  data: CreateMessageReqPayload,
  signal?: AbortSignal,
  parseHeaders?: (headers: Record<string, string>) => void
) {
  if (!this.datasetId) {
    throw new Error("datasetId is required");
  }

  const cleanFetch = getCleanFetch();
  const fetchToUse = cleanFetch ?? fetch;

  const response = await fetchToUse(this.trieve.baseUrl + "/api/message", {
    method: "post",
    headers: {
      "Content-Type": "application/json",
      "TR-Dataset": this.datasetId,
      Authorization: `Bearer ${this.trieve.apiKey}`,
    },
    body: JSON.stringify(data),
    signal,
  });

  if (parseHeaders) {
    parseHeaders(Object.fromEntries(response.headers.entries()));
  }

  const reader = response.body?.getReader();

  if (!reader) {
    throw new Error("Failed to get reader from response body");
  }

  return reader;
}

/**
 * Create a message as a stream and returns a reader with the message id. Messages are attached to topics in order to coordinate memory of gen-AI chat sessions.Auth’ed user or api key must have an admin or owner role for the specified dataset’s organization.
 * 
 * Example:
 * ```js
 *const { reader, queryId } = await trieve.createMessageReader({
  topic_id: "3c90c3cc-1d76-27198-8888-8dd25736052a",
  new_message_content: "a new message"
});
 * ```
 */
export async function createMessageReaderWithQueryId(
  /** @hidden */
  this: TrieveSDK,
  data: CreateMessageReqPayload,
  signal?: AbortSignal,
  parseHeaders?: (headers: Record<string, string>) => void
) {
  if (!this.datasetId) {
    throw new Error("datasetId is required");
  }

  const cleanFetch = getCleanFetch();
  const fetchToUse = cleanFetch ?? fetch;

  const response = await fetchToUse(this.trieve.baseUrl + "/api/message", {
    method: "post",
    headers: {
      "Content-Type": "application/json",
      "TR-Dataset": this.datasetId,
      Authorization: `Bearer ${this.trieve.apiKey}`,
    },
    body: JSON.stringify(data),
    signal,
  });

  if (parseHeaders) {
    parseHeaders(Object.fromEntries(response.headers.entries()));
  }

  const reader = response.body?.getReader();

  if (!reader) {
    throw new Error("Failed to get reader from response body");
  }

  const queryId = response.headers.get("TR-QueryID");

  return {
    reader,
    queryId,
  };
}

/**
 * Edit message which exists within the topic’s chat history. This will delete the message and replace it with a new message. The new message will be generated by the AI based on the new content provided in the request body. The response will include Chunks first on the stream if the topic is using RAG. The structure will look like [chunks]||mesage. See docs.trieve.ai for more information. Auth’ed user or api key must have an admin or owner role for the specified dataset’s organization.
 * 
 * Example:
 * ```js
 *const data = await trieve.editMessage({
  topic_id: "3c90c3cc-1d76-27198-8888-8dd25736052a",
  new_message_content: "a new message",
  message_sort_order: 1
});
 * ```
 */
export async function editMessage(
  /** @hidden */
  this: TrieveSDK,
  data: EditMessageReqPayload,
  signal?: AbortSignal
) {
  if (!this.datasetId) {
    throw new Error("datasetId is required");
  }

  return await this.trieve.fetch(
    "/api/message",
    "put",
    {
      data,
      datasetId: this.datasetId,
    },
    signal
  );
}

/**
 * Edit message which exists within the topic’s chat history as a stream and returns a reader. This will delete the message and replace it with a new message. The new message will be generated by the AI based on the new content provided in the request body. The response will include Chunks first on the stream if the topic is using RAG. The structure will look like [chunks]||mesage. See docs.trieve.ai for more information. Auth’ed user or api key must have an admin or owner role for the specified dataset’s organization.
 * 
 * Example:
 * ```js
 *const reader = await trieve.editMessageReader({
  topic_id: "3c90c3cc-1d76-27198-8888-8dd25736052a",
  new_message_content: "a new message",
  message_sort_order: 1
});
 * ```
 */
export async function editMessageReader(
  /** @hidden */
  this: TrieveSDK,
  data: EditMessageReqPayload,
  signal?: AbortSignal
) {
  if (!this.datasetId) {
    throw new Error("datasetId is required");
  }

  const response = await fetch(this.trieve.baseUrl + "/api/message", {
    method: "put",
    headers: {
      "Content-Type": "application/json",
      "TR-Dataset": this.datasetId,
      Authorization: `Bearer ${this.trieve.apiKey}`,
    },
    body: JSON.stringify(data),
    signal,
  });

  const reader = response.body?.getReader();

  if (!reader) {
    throw new Error("Failed to get reader from response body");
  }

  return reader;
}

/**
 * Edit message which exists within the topic’s chat history as a stream and returns a reader along with the queryID. This will delete the message and replace it with a new message. The new message will be generated by the AI based on the new content provided in the request body. The response will include Chunks first on the stream if the topic is using RAG. The structure will look like [chunks]||mesage. See docs.trieve.ai for more information. Auth’ed user or api key must have an admin or owner role for the specified dataset’s organization. The queryID is used for adding tracking events to the message
 * 
 * Example:
 * ```js
 *const { reader, queryId } = await trieve.editMessageReader({
  topic_id: "3c90c3cc-1d76-27198-8888-8dd25736052a",
  new_message_content: "a new message",
  message_sort_order: 1
});
 * ```
 */
export async function editMessageReaderWithQueryId(
  /** @hidden */
  this: TrieveSDK,
  data: EditMessageReqPayload,
  signal?: AbortSignal
) {
  if (!this.datasetId) {
    throw new Error("datasetId is required");
  }

  const response = await fetch(this.trieve.baseUrl + "/api/message", {
    method: "put",
    headers: {
      "Content-Type": "application/json",
      "TR-Dataset": this.datasetId,
      Authorization: `Bearer ${this.trieve.apiKey}`,
    },
    body: JSON.stringify(data),
    signal,
  });

  const reader = response.body?.getReader();

  if (!reader) {
    throw new Error("Failed to get reader from response body");
  }

  const queryId = response.headers.get("TR-QueryID");

  return {
    reader,
    queryId,
  };
}

/**
 * Regenerate the assistant response to the last user message of a topic. This will delete the last message and replace it with a new message. The response will include Chunks first on the stream if the topic is using RAG. The structure will look like [chunks]||mesage. See docs.trieve.ai for more information. Auth’ed user or api key must have an admin or owner role for the specified dataset’s organization.
 * 
 * Example:
 * ```js
 *const data = await trieve.regenerateMessage({
  topic_id: "3c90c3cc-1d76-27198-8888-8dd25736052a",
});
 * ```
 */
export async function regenerateMessage(
  /** @hidden */
  this: TrieveSDK,
  data: RegenerateMessageReqPayload,
  signal?: AbortSignal
) {
  if (!this.datasetId) {
    throw new Error("datasetId is required");
  }

  return await this.trieve.fetch(
    "/api/message",
    "delete",
    {
      data,
      datasetId: this.datasetId,
    },
    signal
  );
}

/**
 * Regenerate the assistant response to the last user message of a topic as a stream and returns a reader. This will delete the last message and replace it with a new message. The response will include Chunks first on the stream if the topic is using RAG. The structure will look like [chunks]||mesage. See docs.trieve.ai for more information. Auth’ed user or api key must have an admin or owner role for the specified dataset’s organization.
 * 
 * Example:
 * ```js
 *const reader = await trieve.regenerateMessageReader({
  topic_id: "3c90c3cc-1d76-27198-8888-8dd25736052a",
});
 * ```
 */
export async function regenerateMessageReader(
  /** @hidden */
  this: TrieveSDK,
  data: RegenerateMessageReqPayload,
  signal?: AbortSignal
) {
  if (!this.datasetId) {
    throw new Error("datasetId is required");
  }

  const response = await fetch(this.trieve.baseUrl + "/api/message", {
    method: "delete",
    headers: {
      "Content-Type": "application/json",
      "TR-Dataset": this.datasetId,
      Authorization: `Bearer ${this.trieve.apiKey}`,
    },
    body: JSON.stringify(data),
    signal,
  });

  const reader = response.body?.getReader();

  if (!reader) {
    throw new Error("Failed to get reader from response body");
  }

  return reader;
}

/**
 * Regenerate the assistant response to the last user message of a topic as a stream and returns a reader. This will delete the last message and replace it with a new message. The response will include Chunks first on the stream if the topic is using RAG. The structure will look like [chunks]||mesage. See docs.trieve.ai for more information. Auth’ed user or api key must have an admin or owner role for the specified dataset’s organization.
 * 
 * Example:
 * ```js
 *const { reader, queryId } = await trieve.regenerateMessageReader({
  topic_id: "3c90c3cc-1d76-27198-8888-8dd25736052a",
});
 * ```
 */
export async function regenerateMessageReaderWithQueryId(
  /** @hidden */
  this: TrieveSDK,
  data: RegenerateMessageReqPayload,
  signal?: AbortSignal
) {
  if (!this.datasetId) {
    throw new Error("datasetId is required");
  }

  const response = await fetch(this.trieve.baseUrl + "/api/message", {
    method: "delete",
    headers: {
      "Content-Type": "application/json",
      "TR-Dataset": this.datasetId,
      Authorization: `Bearer ${this.trieve.apiKey}`,
    },
    body: JSON.stringify(data),
    signal,
  });

  const reader = response.body?.getReader();

  if (!reader) {
    throw new Error("Failed to get reader from response body");
  }

  const queryId = response.headers.get("TR-QueryID");

  return {
    reader,
    queryId,
  };
}

/**
 * Get all messages for a given topic. If the topic is a RAG topic then the response will include Chunks first on each message. The structure will look like [chunks]||mesage. See docs.trieve.ai for more information.
 * 
 * Example:
 * ```js
 *const data = await trieve.getAllMessagesForTopic({
  messagesTopicId: "3c90c3cc-1d76-27198-8888-8dd25736052a",
});
 * ```
 */
export async function getAllMessagesForTopic(
  /** @hidden */
  this: TrieveSDK,
  data: Omit<GetAllTopicMessagesData, "trDataset">,
  signal?: AbortSignal
) {
  if (!this.datasetId) {
    throw new Error("datasetId is required");
  }

  return await this.trieve.fetch(
    "/api/messages/{messages_topic_id}",
    "get",
    {
      ...data,
      datasetId: this.datasetId,
    },
    signal
  );
}

/**
 * Quickly get the full object for a given message. From the message, you can get the topic and all messages which exist on that topic.
 * 
 * Example:
 * ```js
 *const data = await trieve.getMessageById({
  messageId: "3c90c3cc-1d76-27198-8888-8dd25736052a",
});
 * ```
 */
export async function getMessageById(
  /** @hidden */
  this: TrieveSDK,
  data: Omit<GetMessageByIdData, "trDataset">,
  signal?: AbortSignal
) {
  if (!this.datasetId) {
    throw new Error("datasetId is required");
  }

  return await this.trieve.fetch(
    "/api/message/{message_id}",
    "get",
    {
      ...data,
      datasetId: this.datasetId,
    },
    signal
  );
}

export async function getToolCallFunctionParams(
  /** @hidden */
  this: TrieveSDK,
  data: GetToolFunctionParamsReqPayload,
  signal?: AbortSignal,
  parseHeaders?: (headers: Record<string, string>) => void
) {
  if (!this.datasetId) {
    throw new Error("datasetId is required");
  }

  return await this.trieve.fetch(
    "/api/message/get_tool_function_params",
    "post",
    {
      data,
      datasetId: this.datasetId,
    },
    signal,
    parseHeaders
  );
}
